一個網頁應用程式的生命週期可分為以下階段:
瀏覽器這邊主要的工作在 4 和 5 這二個階段,讓我們用下面這個 web app 當範例,看看瀏覽器做了哪些事情。
<!DOCTYPE html>
<html>
<head>
<title>Web app lifecycle</title>
</head>
<style>
#first { color: green; }
#second { color: red; }
</style>
<body>
<ul id="first"></ul>
<script>
function addMessage(element, message) {
var messageElement = document.createElement("li");
messageElement.textContent = message;
element.appendChild(messageElement);
}
var first = document.getElementById("first");
addMessage(first, "Page loading");
</script>
<ul id="second"></ul>
<script>
document.body.addEventListener("mousemove", function() {
var second = document.getElementById("second");
addMessage(second, "Event: mousemove");
});
document.body.addEventListener("click", function() {
var second = document.getElementById("second");
addMessage(second, "Event: click");
});
</script>
</body>
</html>
在這個簡單的 web app 有二個<ul>
元素,id
分別為first
和second
,並用不同的 CSS 規則上色區分。
第一段 JavaScript 定義一個 addMessage
函式,會在給定的元素裡新增一個<li>
元素並塞一段訊息文字。
在網頁載入時我們取得 #first
元素,將訊息文字附加在其中。
第二段 JavaScript 在頁面建立二個 event listeners,分別監聽 mousemove
及 click
事件,事件發生時在 #second
裡增加對應的訊息文字。
讓我們先從頁面建置階段開始看,在這個階段瀏覽器主要做了二件事:
HTML 是由不同的元素(element)組成一個樹狀結構資料,瀏覽器會一一解析這些元素並建立對應的 DOM (Document Object Model 文件物件模型)節點,DOM 建好後瀏覽器會按照這個模型建立頁面。
HTML 元素是一組由相同名稱的標籤組合而成的資料結構。
(圖片來源MDN)
DOM 是 HTML 的結構化表示形式,其中每個 HTML 元素都表示為一個節點(node)。上一層節點稱為 parent node,下一層節點叫做 child node,每個節點一定會有一個 parent node,除了最外層的根節點<html>
之外。一個節點可以有任何數量的 child nodes,擁有同樣 parent node 的節點彼此之間屬於 sibling nodes。
在我們的範例裡,<head>
和<body>
是<html>
的 child nodes;<html>
是它們的 parent node;<head>
和<body>
互相是對方的 sibling node。
我們常把節點也叫做元素,實際上元素是節點的一種,只不過在 DOM 裡面為數眾多,而且我們操作 DOM 經常是在操作元素,所以會常常在說明文件裡看到這二個名詞互相替代。
雖然說 DOM 是參考 HTML 建立的,但二者是不同的東西,我們可以把 HTML 當做建築物的藍圖,DOM 是建築物的主要結構,這樣子比較容易分辨。而且在建立 DOM 的過程中,瀏覽器有辦法修正 HTML 的問題,建立有效的 DOM。讓我們看一個例子。
<html>
<head>
<p>hello</p>
</head>
<body>
</body>
</html>
在這個錯誤的 HTML 裡,我們不小心把<p>
放到<head>
裡面,照這樣產生的 DOM,p
節點會是head
的 child node。
但是我們看到瀏覽器的頁面,<p>
卻是出現在<body>
裡,也就是說瀏覽器發現 HTML 的錯誤,修正 DOM 把p
修正為body
的 child node。
當遇到 <script>
這個特殊元素時,瀏覽器會停止解析 DOM 並切換到步驟二,執行裡面的 JavaScript 程式。
瀏覽器提供了一些操作介面讓 JavaScript 能與頁面互動,包含一個全域window
物件以及瀏覽器 API。
全域的 window
物件,代表頁面所屬的瀏覽器視窗,可藉由它來存取所有其他的全域物件、全域變數及瀏覽器API。這個物件會一直存在,直到頁面關閉。
window
之中最重要的屬性是document
,也是目前頁面的 DOM 結構,透過document
,JavaScript 程式碼可以任意改變所在頁面的 DOM 結構,像是修改或刪除現有元素,甚至是建立及插入新的元素。
例如範例中的一段程式碼:
var first = document.getElementById("first");
我們用全域物件window
中的document
取得目前頁面的 DOM 結構,在其中找出具有id="first"
的元素,然後把它指派給first
變數。
運用window
裡的屬性或方法時,window
可以省略不寫。
JavaScript 引擎會從第一行開始一行一行的執行程式碼,在函式裡的程式碼要等到函式被呼叫後才會執行。
此時 JavaScript 程式碼可以任意改變現有的 DOM 結構,我們在取得了first
元素後,呼叫addMessage
函式,並把first
和一段文字訊息當參數代入,此時開始執行函式裡的程式碼,先建立一個li
元素,將代入的文字訊息指派給元素的textContent
屬性,最後再將這個新元素附加在代入的first
元素之下,成為它的 child node。
(執行script
裡的程式碼後,新的 DOM 結構)
雖然說 JavaScript 可以任意變動 DOM,但它無法存取尚未建立的節點,像是在第一段<script>
裡要存取#second
會得到null
的值,因為這個時候它還沒有出現在 DOM 裡。這也是我們會把<script>
放在頁面底部的緣故,因為這樣就可以確保程式裡的 HTML 元素都已經在 DOM 裡了。
執行完<script>
裡最後一行程式碼後,瀏覽器會回到解析剩下的 HTML 繼續建立 DOM,如果遇到新的 <script>
元素會再重複相同過程。
這裡的重點是在script
元素中建立的全域變數,會一直保持全域的狀態,因為全域變數是依附在window
之下,而window
在頁面生命週期結束前一直都是有效的。
下一篇文章繼續看生命週期的第二階段: 事件處理。
當遇到 這個特殊元素時,瀏覽器會停止解析 DOM 並切換到步驟二,執行裡面的 JavaScript 程式。
您好! 不好意思,想詢問這段話中「步驟二」是指文中哪個部分的步驟二呢? 謝謝您